package edpool;

import harness.Queue;
import harness.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; 
import java.util.logging.Level;
import java.util.logging.Logger;

public class EDPool<T> implements Queue
{
      final private DiffractingEliminationTree<T> countingTree;
      final private LinkedBlockingQueue<T>[]  poolArray;
      final private GlobalExchangeStates<T> globalExchangeStates = new GlobalExchangeStates<T>();
       
      ThreadLocal<ProducerPackage<T>> producerPackage = 
    	  new ThreadLocal<ProducerPackage<T>>()
    	      {
    	  		protected ProducerPackage<T> initialValue() 
    	  		{ return new ProducerPackage<T>(globalExchangeStates);}
    	      };
    	      
      ThreadLocal<ConsumerPackage<T>> consumerPackage = 
    	  new ThreadLocal<ConsumerPackage<T>>()
    	      {
    	  		protected ConsumerPackage<T> initialValue() 
    	  		{ return new ConsumerPackage<T>(globalExchangeStates);}
    	      };
      			    
      public EDPool (int numberOfQueuesAtLeafs, int topLevelPrismCapacity, int numOfTreeLevels) 
      {
    	  
      	  this.countingTree = new DiffractingEliminationTree<T>(topLevelPrismCapacity, numOfTreeLevels);
         
      	  poolArray = new LinkedBlockingQueue[numberOfQueuesAtLeafs];
          for (int i=0; i<numberOfQueuesAtLeafs; i++)
        	  poolArray[i] = new LinkedBlockingQueue<T>();
  
      }
      
      public void put(T value) throws InterruptedException
      {
    	  	ProducerPackage<T> myPackage = producerPackage.get();
    	  	try
    	  	{
	    	  		myPackage.initialize(value);
	    	  	
	            
	    	  		int index = countingTree.Traverse(myPackage);
	    	  		if (myPackage.getEliminationState() == EliminationStateEnum.Eliminated)
	     	  			return;
	      	  		
	  		  	  	int poolIndex  = index  % poolArray.length;
	
	  		  	  	poolArray[poolIndex].put(value);
     	  	}
    	  	finally
    	  	{
    	  		myPackage.clear();
    	  	}
             
      }
      public T get() throws InterruptedException
      {
    	  ConsumerPackage<T> myPackage = consumerPackage.get();
    	  try
		  {
			  while(true)
			  {
		    	  myPackage.initialize();
			  	  int index = countingTree.Traverse(myPackage);
			   
			  	  if (myPackage.getEliminationState() == EliminationStateEnum.Eliminated)
			  		  return myPackage.getValue(); 
	 		    
	 
			  		  
			  	  int poolIndex  = index  % poolArray.length;
			
 			  	  T result = poolArray[poolIndex].poll(Constants.timeout, TimeUnit.MILLISECONDS);	  	  
			  	  if (result != null)
			  		  return result;
			  }
    	  }
    	  finally
    	  {
    		  myPackage.clear();
    	  }
      }

    public Object poll(int threadId, Random rng) {
        try {
            return get();
        } catch (InterruptedException ex) {
            System.err.println("CAUGHT INTERRUPTED EXCEPTION IN EDPOOL's GET ROUTINE");
        }
        return null;
    }

    public boolean offer(Object e, int threadId, Random rng) {
        try {
            put((T) e);
            return true;
        } catch (InterruptedException ex) {
            System.err.println("CAUGHT INTERRUPTED EXCEPTION IN EDPOOL's PUT ROUTINE");
        }
        return false;
    }
}

